1   // Licensed under the Apache License, Version 2.0 (the "License");
2   // you may not use this file except in compliance with the License.
3   // You may obtain a copy of the License at
4   //
5   // http://www.apache.org/licenses/LICENSE-2.0
6   //
7   // Unless required by applicable law or agreed to in writing, software
8   // distributed under the License is distributed on an "AS IS" BASIS,
9   // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10  // See the License for the specific language governing permissions and
11  // limitations under the License.
12  
13  package org.apache.tapestry5.internal.services.assets;
14  
15  import org.apache.tapestry5.internal.services.ResourceStreamer;
16  import org.apache.tapestry5.ioc.IOOperation;
17  import org.apache.tapestry5.ioc.OperationTracker;
18  import org.apache.tapestry5.services.LocalizationSetter;
19  import org.apache.tapestry5.services.Request;
20  import org.apache.tapestry5.services.Response;
21  import org.apache.tapestry5.services.assets.AssetRequestHandler;
22  import org.apache.tapestry5.services.assets.StreamableResource;
23  import org.apache.tapestry5.services.javascript.JavaScriptStack;
24  import org.apache.tapestry5.services.javascript.JavaScriptStackSource;
25  import org.slf4j.Logger;
26  
27  import java.io.IOException;
28  import java.util.regex.Matcher;
29  import java.util.regex.Pattern;
30  
31  public class StackAssetRequestHandler implements AssetRequestHandler
32  {
33      private final Logger logger;
34  
35      private final LocalizationSetter localizationSetter;
36  
37      private final ResourceStreamer resourceStreamer;
38  
39      // Group 1: checksum
40      // Group 2: locale
41      // Group 3: path
42      private final Pattern pathPattern = Pattern.compile("^(.+)/(.+)/(.+)\\.js$");
43  
44      private final OperationTracker tracker;
45  
46      private final JavaScriptStackAssembler javaScriptStackAssembler;
47  
48      private final JavaScriptStackSource stackSource;
49  
50      public StackAssetRequestHandler(Logger logger, LocalizationSetter localizationSetter,
51                                      ResourceStreamer resourceStreamer,
52                                      OperationTracker tracker,
53                                      JavaScriptStackAssembler javaScriptStackAssembler,
54                                      JavaScriptStackSource stackSource)
55      {
56          this.logger = logger;
57          this.localizationSetter = localizationSetter;
58          this.resourceStreamer = resourceStreamer;
59          this.tracker = tracker;
60          this.javaScriptStackAssembler = javaScriptStackAssembler;
61          this.stackSource = stackSource;
62      }
63  
64      public boolean handleAssetRequest(Request request, Response response, final String extraPath) throws IOException
65      {
66          return tracker.perform(String.format("Streaming JavaScript asset stack %s", extraPath),
67                  new IOOperation<Boolean>()
68                  {
69                      public Boolean perform() throws IOException
70                      {
71                          return streamStackResource(extraPath);
72                      }
73                  });
74      }
75  
76      private boolean streamStackResource(String extraPath) throws IOException
77      {
78          Matcher matcher = pathPattern.matcher(extraPath);
79  
80          if (!matcher.matches())
81          {
82              logger.warn(String.format("Unable to parse '%s' as an asset stack path", extraPath));
83  
84              return false;
85          }
86  
87          String checksum = matcher.group(1);
88          String localeName = matcher.group(2);
89          final String stackName = matcher.group(3);
90  
91          final boolean compressed = checksum.startsWith("z");
92  
93          if (compressed)
94          {
95              checksum = checksum.substring(1);
96          }
97  
98          final JavaScriptStack stack = stackSource.findStack(stackName);
99  
100         if (stack == null)
101         {
102             logger.warn(String.format("JavaScript stack '%s' not found.", stackName));
103             return false;
104         }
105 
106 
107         // Yes, I have a big regret that the JavaScript stack stuff relies on this global, rather than
108         // having it passed around properly.
109 
110         localizationSetter.setNonPersistentLocaleFromLocaleName(localeName);
111 
112         StreamableResource resource =
113                 tracker.perform(String.format("Assembling JavaScript asset stack '%s' (%s)",
114                                 stackName, localeName),
115                         new IOOperation<StreamableResource>()
116                         {
117                             public StreamableResource perform() throws IOException
118                             {
119 
120                                 return javaScriptStackAssembler.assembleJavaScriptResourceForStack(stackName, compressed,
121                                         stack.getJavaScriptAggregationStrategy());
122 
123                             }
124                         });
125 
126 
127         if (resource == null)
128         {
129             return false;
130         }
131 
132         return resourceStreamer.streamResource(resource, checksum, ResourceStreamer.DEFAULT_OPTIONS);
133     }
134 }